package com.dennytech.tac.base;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.UUID;

import org.apache.http.HttpHost;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;

import com.dennytech.tac.utils.Log;

public final class Environment {

	private static final String LOG_TAG =Environment.class.getSimpleName();
	
	private static String oldUdid; // uuid did in 3.x
	private static boolean oldUdidInited;

	private static String imei;

	private static String imsi;
	private static String imsiIdentity;

	private static String phone;
	private static boolean phoneInited;

	private static String source;
	private static boolean sourceInited;
	private static String source2;

	private static String mapiUserAgent;

	private static String screenInfo;

	private static PackageInfo packageInfo;

	private ConnectivityManager connManager;

	public static void init(Context context) {
		env = new Environment(context);
	}

	private static Environment env;

	public static Environment instance() {
		return env;
	}

	private Environment(Context context) {
		connManager = (ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE);
	}

	public static String WIFI_HTTP_PROXY = null;
	public static int WIFI_HTTP_PROXY_PORT = 0;

	public static HttpHost globalProxy() {
		if (env == null) {
			Log.w(LOG_TAG, "Environment has not be inited.");
		}
		
		if (env.connManager == null)
			return null;
		NetworkInfo activeNetInfo = env.connManager.getActiveNetworkInfo();
		if (activeNetInfo == null)
			return null;
		if (activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
			if (WIFI_HTTP_PROXY == null)
				return null;
			return new HttpHost(WIFI_HTTP_PROXY, WIFI_HTTP_PROXY_PORT);
		}
		if (activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
			String extraInfo = activeNetInfo.getExtraInfo();
			if (extraInfo == null)
				return null;
			extraInfo = extraInfo.toLowerCase();
			if (extraInfo.contains("cmnet"))
				return null;
			if (extraInfo.contains("cmwap"))
				return new HttpHost("10.0.0.172");
			if (extraInfo.contains("3gnet"))
				return null;
			if (extraInfo.contains("3gwap"))
				return new HttpHost("10.0.0.172");
			if (extraInfo.contains("uninet"))
				return null;
			if (extraInfo.contains("uniwap"))
				return new HttpHost("10.0.0.172");
			if (extraInfo.contains("ctnet"))
				return null;
			if (extraInfo.contains("ctwap"))
				return new HttpHost("10.0.0.200");
			if (extraInfo.contains("#777")) {
				try {
					Cursor c = CFApplication
							._instance()
							.getContentResolver()
							.query(Uri
									.parse("content://telephony/carriers/preferapn"),
									new String[] { "proxy", "port" }, null,
									null, null);
					if (c.moveToFirst()) {
						String host = c.getString(0);
						if (host.length() > 3) {
							int port = 0;
							try {
								port = Integer.parseInt(c.getString(1));
							} catch (NumberFormatException e) {
							}
							return new HttpHost(host, port > 0 ? port : 80);
						}
					}
					return null;
				} catch (Exception e) {
					return null;
				}
			}
		}
		return null;
	}

	private static String escapeSource(String src) {
		StringBuilder sb = new StringBuilder();
		for (char c : src.toCharArray()) {
			if (c >= 'a' && c <= 'z') {
				sb.append(c);
			} else if (c >= 'A' && c <= 'Z') {
				sb.append(c);
			} else if (c >= '0' && c <= '9') {
				sb.append(c);
			} else if (c == '.' || c == '_' || c == '-' || c == '/') {
				sb.append(c);
			} else if (c == ' ') {
				sb.append('_');
			}
		}
		return sb.toString();
	}

	private static PackageInfo pkgInfo() {
		if (packageInfo == null) {
			try {
				Context c = CFApplication._instance();
				packageInfo = c.getPackageManager().getPackageInfo(
						c.getPackageName(), 0);
			} catch (NameNotFoundException e) {
			}
		}

		return packageInfo;
	}

	private static String oldUdid() {
		if (!oldUdidInited && CFApplication._instance() != null) {
			Context c = CFApplication._instance();
			SharedPreferences prefs = c.getSharedPreferences(
					c.getPackageName(), Context.MODE_PRIVATE);
			String str = prefs.getString("deviceId", null);
			if (str != null) {
				// the old udid should look like UDID.
				// but the 4.x version override this key to imei.
				// so we just need the old udid
				try {
					UUID.fromString(str);
				} catch (Exception e) {
					prefs.edit().remove("deviceId").commit();
					str = null;
				}
			}
			oldUdid = str;
			oldUdidInited = true;
		}
		return oldUdid;
	}

	/**
	 * 设备的唯一标识
	 * <p>
	 * 4.0以后的新设备deviceId为IMEI<br>
	 * 4.0之前的设备deviceId为UUID，IMEI在deviceId2中存放
	 */
	public static String deviceId() {
		String oldUdid = oldUdid();
		if (oldUdid != null) {
			return oldUdid;
		} else {
			return imei();
		}
	}

	/**
	 * deviceId2只作为统计系统使用，业务不应该调用
	 * <p>
	 * 4.0以后的新设备deviceId2为空<br>
	 * 4.0之前的设备deviceId2为IMEI
	 */
	public static String deviceId2() {
		String oldUdid = oldUdid();
		if (oldUdid != null) {
			return imei();
		} else {
			return null;
		}
	}

	// /**
	// * 当前的sessionId。会在运行过程中发生变化
	// */
	// public static String sessionId() {
	// CFApplication c = CFApplication._instance();
	// if (c == null)
	// return "";
	// return c.sessionId();
	// }

	/**
	 * 手机的IMEI设备序列号
	 * <p>
	 * 第一次启动时会保存该序列号，可以频繁调用
	 * 
	 * @return IMEI or "00000000000000" if error
	 */
	public static String imei() {
		if (imei == null) {
			// update cached imei when identity changed. including brand, model,
			// radio and system version
			String deviceIdentity = Build.VERSION.RELEASE + ";" + Build.MODEL
					+ ";" + Build.BRAND;
			if (deviceIdentity.length() > 64) {
				deviceIdentity = deviceIdentity.substring(0, 64);
			}
			if (deviceIdentity.indexOf('\n') >= 0) {
				deviceIdentity = deviceIdentity.replace('\n', ' ');
			}

			String cachedIdentity = null;
			String cachedImei = null;
			try {
				// do not use file storage, use cached instead
				File path = new File(CFApplication._instance().getCacheDir(),
						"cached_imei");
				FileInputStream fis = new FileInputStream(path);
				byte[] buf = new byte[1024];
				int l = fis.read(buf);
				fis.close();
				String str = new String(buf, 0, l, "UTF-8");
				int a = str.indexOf('\n');
				cachedIdentity = str.substring(0, a);
				int b = str.indexOf('\n', a + 1);
				cachedImei = str.substring(a + 1, b);
			} catch (Exception e) {
			}

			if (deviceIdentity.equals(cachedIdentity)) {
				imei = cachedImei;
			} else {
				imei = null;
			}

			// cache fail, read from telephony manager
			if (imei == null) {
				try {
					TelephonyManager tel = (TelephonyManager) CFApplication
							._instance().getSystemService(
									Context.TELEPHONY_SERVICE);
					imei = tel.getDeviceId();
					if (imei != null) {
						if (imei.length() < 8) {
							imei = null;
						} else {
							char c0 = imei.charAt(0);
							boolean allSame = true;
							for (int i = 0, n = imei.length(); i < n; i++) {
								if (c0 != imei.charAt(i)) {
									allSame = false;
									break;
								}
							}
							if (allSame)
								imei = null;
						}
					}
				} catch (Exception e) {
				}
				if (imei != null) {
					try {
						File path = new File(CFApplication._instance()
								.getCacheDir(), "cached_imei");
						FileOutputStream fos = new FileOutputStream(path);
						String str = deviceIdentity + "\n" + imei + "\n";
						fos.write(str.getBytes("UTF-8"));
						fos.close();
					} catch (Exception e) {
					}
				} else {
					File path = new File(CFApplication._instance()
							.getCacheDir(), "cached_imei");
					path.delete();
				}
			}

			if (imei == null) {
				imei = "00000000000000";
			}
		}
		return imei;
	}

	/**
	 * 手机的SIM卡序列号
	 * <p>
	 * 读取后会缓存序列号，当手机运营商改变时会重新读取<br>
	 * 避免频繁调用
	 * 
	 * @return IMSI or null
	 */
	public synchronized static String imsi() {
		String op = "";
		try {
			TelephonyManager tel = (TelephonyManager) CFApplication._instance()
					.getSystemService(Context.TELEPHONY_SERVICE);
			op = tel.getNetworkOperator();
		} catch (Exception e) {
		}

		// update cached imei when identity changed. including brand, model,
		// radio and system version
		String deviceIdentity = op + ";" + Build.VERSION.RELEASE + ";"
				+ Build.MODEL + ";" + Build.BRAND;
		if (deviceIdentity.length() > 64) {
			deviceIdentity = deviceIdentity.substring(0, 64);
		}
		if (deviceIdentity.indexOf('\n') >= 0) {
			deviceIdentity = deviceIdentity.replace('\n', ' ');
		}

		if (imsiIdentity == null) {
			try {
				// do not use file storage, use cached instead
				File path = new File(CFApplication._instance().getCacheDir(),
						"cached_imsi");
				FileInputStream fis = new FileInputStream(path);
				byte[] buf = new byte[1024];
				int l = fis.read(buf);
				fis.close();
				String str = new String(buf, 0, l, "UTF-8");
				int a = str.indexOf('\n');
				String cachedIdentity = str.substring(0, a);
				int b = str.indexOf('\n', a + 1);
				String cachedImsi = str.substring(a + 1, b);
				imsiIdentity = cachedIdentity;
				if (deviceIdentity.equals(cachedIdentity)) {
					imsi = cachedImsi;
				} else {
					imsi = null;
				}
			} catch (Exception e) {
			}
		}

		if (!deviceIdentity.equals(imsiIdentity)) {
			try {
				TelephonyManager tel = (TelephonyManager) CFApplication
						._instance()
						.getSystemService(Context.TELEPHONY_SERVICE);
				imsi = tel.getSubscriberId();
				imsiIdentity = deviceIdentity;
				if (imsi != null) {
					File path = new File(CFApplication._instance()
							.getCacheDir(), "cached_imsi");
					FileOutputStream fos = new FileOutputStream(path);
					String str = deviceIdentity + "\n" + imsi + "\n";
					fos.write(str.getBytes("UTF-8"));
					fos.close();
				} else {
					File path = new File(CFApplication._instance()
							.getCacheDir(), "cached_imsi");
					path.delete();
				}
			} catch (Exception e) {
			}
		}

		return imsi;
	}

	/**
	 * 取手机号。某些系统可能无法返回。不鼓励使用
	 * 
	 * @return tel.getLine1Number()
	 */
	public static String phone() {
		if (!phoneInited) {
			String op = "";
			try {
				TelephonyManager tel = (TelephonyManager) CFApplication
						._instance()
						.getSystemService(Context.TELEPHONY_SERVICE);
				op = tel.getNetworkOperator();
			} catch (Exception e) {
			}

			// update cached imei when identity changed. including brand, model,
			// radio and system version
			String deviceIdentity = op + ";" + Build.VERSION.RELEASE + ";"
					+ Build.MODEL + ";" + Build.BRAND;
			if (deviceIdentity.length() > 64) {
				deviceIdentity = deviceIdentity.substring(0, 64);
			}
			if (deviceIdentity.indexOf('\n') >= 0) {
				deviceIdentity = deviceIdentity.replace('\n', ' ');
			}

			String cachedIdentity = null;
			String cachedPhone = null;
			try {
				// do not use file storage, use cached instead
				File path = new File(CFApplication._instance().getCacheDir(),
						"cached_phone");
				FileInputStream fis = new FileInputStream(path);
				byte[] buf = new byte[1024];
				int l = fis.read(buf);
				fis.close();
				String str = new String(buf, 0, l, "UTF-8");
				int a = str.indexOf('\n');
				cachedIdentity = str.substring(0, a);
				int b = str.indexOf('\n', a + 1);
				cachedPhone = str.substring(a + 1, b);
			} catch (Exception e) {
			}

			if (deviceIdentity.equals(cachedIdentity)) {
				phone = cachedPhone;
			} else {
				phone = null;
			}

			// cache fail, read from telephony manager
			if (phone == null) {
				try {
					TelephonyManager tel = (TelephonyManager) CFApplication
							._instance().getSystemService(
									Context.TELEPHONY_SERVICE);
					phone = tel.getLine1Number();
				} catch (Exception e) {
				}
				if (phone != null && phone.length() > 6) {
					try {
						File path = new File(CFApplication._instance()
								.getCacheDir(), "cached_phone");
						FileOutputStream fos = new FileOutputStream(path);
						String str = deviceIdentity + "\n" + phone + "\n";
						fos.write(str.getBytes("UTF-8"));
						fos.close();
					} catch (Exception e) {
					}
				} else {
					File path = new File(CFApplication._instance()
							.getCacheDir(), "cached_phone");
					path.delete();
				}
			}

			phoneInited = true;
		}
		return phone;
	}

	/**
	 * 发布渠道，不允许包含空格（用_替代）及,.;等标点符号
	 * <p>
	 * 常用的有<br>
	 * "androidmarket" = AndroidMarket<br>
	 * "dianping" = http://android.dianping.com/
	 */
	public static String source() {
		if (!sourceInited) {
			try {
				InputStream ins = CFApplication._instance().getAssets()
						.open("source.txt");
				byte[] bytes = new byte[0x100];
				int l = ins.read(bytes);
				if (l > 0) {
					String str = new String(bytes, 0, l);
					source = escapeSource(str);
				}
			} catch (Exception e) {
			}
			sourceInited = true;
		}
		return source;
	}

	/**
	 * 一般为机器的机型，如Nexus_One，不允许包含空格（用_替代）及,.;等标点符号
	 */
	public static String source2() {
		if (source2 == null) {
			source2 = escapeSource(android.os.Build.MODEL);
		}
		return source2;
	}

	/**
	 * 请求MApi服务器使用的UserAgent及pragma-os （Http Header）
	 * <p>
	 * MApi 1.0 (com.dennytech.tac 1.0 androidmarket Nexus_One; Android 2.2)
	 */
	public static String mapiUserAgent() {
		if (mapiUserAgent == null) {
			try {
				Context c = CFApplication._instance();
				PackageInfo packageInfo = c.getPackageManager().getPackageInfo(
						c.getPackageName(), 0);
				StringBuilder sb = new StringBuilder("MApi 1.0 (");
				sb.append(packageInfo.packageName);
				sb.append(" ").append(packageInfo.versionName);

				String source = source();
				if (source != null)
					sb.append(" ").append(source);
				else
					sb.append(" null");

				sb.append(" ").append(source2());
				sb.append("; Android ");
				sb.append(Build.VERSION.RELEASE);
				sb.append(")");

				mapiUserAgent = sb.toString();
			} catch (Exception e) {
				mapiUserAgent = "MApi 1.0 (com.dennytech.tac 1.0; Android "
						+ Build.VERSION.RELEASE + ")";
			}
		}
		return mapiUserAgent;
	}

	public static String screenInfo() {
		if (TextUtils.isEmpty(screenInfo)) {
			Context c = CFApplication._instance();
			DisplayMetrics dm = c.getResources().getDisplayMetrics();
			screenInfo = "screenwidth=" + dm.widthPixels + "&screenheight="
					+ dm.heightPixels + "&screendensity=" + dm.density;
		}

		return screenInfo;
	}

	public static String version() {
		return pkgInfo().versionName;
	}

	public static boolean isDebug() {
		return Log.LEVEL < Integer.MAX_VALUE;
	}

}
